package com.kool.leitu.service;

import com.kool.leitu.base.exception.AppException;
import com.kool.leitu.base.exception.ErrorCodeEnum;
import com.kool.leitu.base.util.DateUtils;
import com.kool.leitu.base.util.StringUtils;
import com.kool.leitu.base.util.ZipUtils;
import com.kool.leitu.bean.ColumnsBean;
import com.kool.leitu.bean.io.CodeResponse;
import com.kool.leitu.bean.vo.ColumnsVo;
import com.kool.leitu.dao.ColumnsDao;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * @description:
 * @author: tianhe
 * @create: 2019/02/11 17:17
 */
@Service
@Slf4j
public class CodeService {
    @Value("${templateDirPath}")
    private String templateDirPath;
    @Value("${generateDirPath}")
    private String generateDirPath;
    @Value("${fileUrl}")
    private String fileUrl;
    @Value("${tableSchema}")
    private String tableSchema;

    @Autowired
    private ColumnsDao columnsDao;

    /**
     * @param pkg
     * @param tables
     * @return
     */
    public CodeResponse genCode(String pkg, String tables) {
        String tmpSubDirPath = generateDirPath + "/"
                + DateUtils.getCurrentTimeString(DateUtils.FORMAT_DATE_AND_TIME_WITHOUT_SPLIT)
                + StringUtils.getUUID();

        File tmpSubDir = new File(tmpSubDirPath);
        if (tmpSubDir.exists() == false) {
            tmpSubDir.mkdirs();
        }
        String[] listTable = tables.split(";");
        for (int i = 0; i < listTable.length; i++) {
            String table = listTable[i];
            generate(table, pkg, tmpSubDirPath);
        }

        String zipName = tmpSubDir.getName() + ".zip";
        String zipFilePath = generateDirPath + "/" + zipName;

        ZipUtils.compress(zipFilePath, tmpSubDirPath);

        String url = fileUrl + zipName;

        CodeResponse codeResponse = new CodeResponse();
        codeResponse.setName(zipName);
        codeResponse.setUrl(url);
        return codeResponse;
    }

    /**
     * 生成文件到指定路径
     *
     * @param table
     * @param pkg
     * @param tempDir
     */
    private void generate(String table, String pkg, String tempDir) {
        //查询表结构
        List<ColumnsBean> columnsBeans = columnsDao.selectByTable(tableSchema, table);


        // freemarker配置
        try {
            Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
            configuration.setDirectoryForTemplateLoading(new File(templateDirPath));
            configuration.setDefaultEncoding("UTF-8");
            configuration.setObjectWrapper(new DefaultObjectWrapper(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS));


            // 获取或创建一个模版。
            Template template = configuration.getTemplate("Dao.ftl");
            // 生成文件到目标文件夹下
            HashMap<String, Object> map = getDaoMap(table, pkg, columnsBeans);
            Writer writer = getDaoWriter(table, getDaoDir(tempDir, pkg));
            template.process(map, writer);

            template = configuration.getTemplate("Xml.ftl");
            map = getXmlMap(table, pkg, columnsBeans);
            writer = getXmlWriter(table, getXmlDir(tempDir, pkg));
            template.process(map, writer);

            template = configuration.getTemplate("Bean.ftl");
            map = getEntityMap(table, pkg, columnsBeans);
            writer = getEntityWriter(table, getEntityDir(tempDir, pkg));
            template.process(map, writer);
        } catch (IOException e) {
            log.error("生成代码错误", e);
            throw new AppException(ErrorCodeEnum.SYSTEM_ERROR.getErrorCode(), e, "生成代码错误");
        } catch (TemplateException e) {
            log.error("生成代码错误", e);
            throw new AppException(ErrorCodeEnum.SYSTEM_ERROR.getErrorCode(), e, "生成代码错误");
        }

    }

    private static HashMap<String, Object> getDaoMap(String tabNam, String pkg, List<ColumnsBean> listRet) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("pkg", pkg);
        map.put("upTumTabNam", StringUtils.transferToHumpName(tabNam, true));
        map.put("lowTumTabNam", StringUtils.transferToHumpName(tabNam, false));

        boolean hasPk = false;
        for (ColumnsBean value : listRet) {
            if ("PRI".equalsIgnoreCase(value.getColumnKey())) {
                hasPk = true;
                break;
            }
        }
        map.put("hasPk", hasPk);
        return map;
    }

    private static Writer getDaoWriter(String tabNam, String descDir) throws IOException {
        String fileName = StringUtils.transferToHumpName(tabNam, true);
        fileName = fileName + "Dao.java";
        String descPath = descDir + "/" + fileName;
        File fDesc = new File(descPath);
        Writer writer = new FileWriter(fDesc);
        return writer;
    }

    private static String getDaoDir(String descDir, String pkg) {
        String dir = descDir + "/" + pkg.replace('.', '/') + "/dao";
        File fDir = new File(dir);
        if (fDir.exists() == false) {
            fDir.mkdirs();
        }
        return dir;
    }

    private static String getXmlDir(String descDir, String pkg) {
        String dir = descDir + "/" + pkg.replace('.', '/') + "/xml";
        File fDir = new File(dir);
        if (fDir.exists() == false) {
            fDir.mkdirs();
        }
        return dir;
    }

    private static Writer getXmlWriter(String tabNam, String descDir) throws IOException {
        String fileName = StringUtils.transferToHumpName(tabNam, true);
        fileName = fileName + "Dao.xml";
        String descPath = descDir + "/" + fileName;
        File fDesc = new File(descPath);
        Writer writer = new FileWriter(fDesc);
        return writer;
    }

    /**
     * @param tabNam
     * @param pkg
     * @param listRet
     * @return
     * @DESCRIBE
     * @DATE 2017-05-01 14:31:02
     */
    private static HashMap<String, Object> getXmlMap(String tabNam, String pkg, List<ColumnsBean> listRet) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("pkg", pkg);
        map.put("upTumTabNam", StringUtils.transferToHumpName(tabNam, true));
        map.put("lowTumTabNam", StringUtils.transferToHumpName(tabNam, false));
        map.put("tabNam", tabNam);

        ArrayList<ColumnsVo> list = new ArrayList<>();
        ArrayList<ColumnsVo> pkList = new ArrayList<>();

        boolean hasPk = false;
        for (ColumnsBean value : listRet) {
            value.setDataType(value.getDataType().toUpperCase());
            ColumnsVo col = new ColumnsVo(value);
            col.setJavaType(ColumnsBean.transferType(value.getDataType()));
            col.setUpJavaName(StringUtils.transferToHumpName(value.getColumnName(), true));
            col.setLowJavaName(StringUtils.transferToHumpName(value.getColumnName(), false));
            list.add(col);

            if ("PRI".equalsIgnoreCase(value.getColumnKey())) {
                hasPk = true;
                pkList.add(col);
            }
        }
        map.put("columns", list);
        map.put("hasPk", hasPk);
        map.put("pkList", pkList);
        return map;
    }

    private static Writer getEntityWriter(String tabNam, String descDir) throws IOException {
        String fileName = StringUtils.transferToHumpName(tabNam, true);
        fileName = fileName + "Bean.java";
        String descPath = descDir + "/" + fileName;
        File fDesc = new File(descPath);
        Writer writer = new FileWriter(fDesc);
        return writer;
    }

    /**
     * @param tabNam
     * @param pkg
     * @param listRet
     * @return
     * @DESCRIBE
     * @DATE 2017-04-29 17:45:05
     */
    private static HashMap<String, Object> getEntityMap(String tabNam, String pkg, List<ColumnsBean> listRet) {
        HashMap<String, Object> map = new HashMap<String, Object>();
        map.put("pkg", pkg);
        map.put("upTumTabNam", StringUtils.transferToHumpName(tabNam, true));
        map.put("lowTumTabNam", StringUtils.transferToHumpName(tabNam, false));
        ArrayList<ColumnsVo> list = new ArrayList<>();
        for (ColumnsBean value : listRet) {
            ColumnsVo col = new ColumnsVo(value);
            col.setJavaType(ColumnsBean.transferType(value.getDataType()));
            col.setUpJavaName(StringUtils.transferToHumpName(value.getColumnName(), true));
            col.setLowJavaName(StringUtils.transferToHumpName(value.getColumnName(), false));
            list.add(col);
        }
        map.put("columns", list);
        return map;
    }

    private static String getEntityDir(String descDir, String pkg) {
        String dir = descDir + "/" + pkg.replace('.', '/') + "/bean";
        File fDir = new File(dir);
        if (fDir.exists() == false) {
            fDir.mkdirs();
        }
        return dir;
    }
}
